﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.ComponentModel;

namespace HyperPin_Database_Editor
{
    public class TableDatabase : BindingList<TableSummary>
    {
        string databaseFileName;
        XmlDocument databaseXmlDoc = null;

        List<TableSummary> tableList;
        List<string> manufacturerList;
        List<string> typeList;
        List<string> yearList;

        public TableDatabase()
        {
            this.AllowNew = true;
            this.AllowRemove = true;
            this.AllowEdit = true;
            this.RaiseListChangedEvents = true;
            this.AddingNew += new AddingNewEventHandler(Custom_AddingNew);
        }

        public virtual void LoadDatabase(string databaseFileName)
        {
            this.databaseFileName = databaseFileName;

            // Load the database file into a local xml document object
            // This object is used our entire lifetime to display and edit
            // the tables that are in the database.
            databaseXmlDoc = new XmlDocument();
            try
            {
                // Load the xml
                databaseXmlDoc.Load(databaseFileName);

                // get a list of all the games
                XmlNodeList xmlGameList = databaseXmlDoc.GetElementsByTagName(TableSummary.xmlTable);
                for (int i = 0; i < xmlGameList.Count; i++)
                {
                    // populate the list of games with the game names
                    // the game name is an attribute of the xml node.
                    XmlNode gameNode = xmlGameList[i];
                    XmlAttributeCollection gameAttributes = gameNode.Attributes;
                    XmlNode gameNameNode = gameAttributes.GetNamedItem(TableSummary.xmlName);

                    TableSummary tableSummary = new TableSummary(gameNode);
                    this.Add(tableSummary);
                }

            }
            catch (System.Exception e)
            {
                MessageBox.Show("Could not parse Visual Pinball database file. The error was: " + e.ToString());
                return;
            }
        }

        public virtual void SaveDatabase()
        {
            // Implemented by subclass to save settings to INI file
            databaseXmlDoc.Save(databaseFileName);
        }

        public virtual void SaveDatabase(string saveFileName)
        {
            // Implemented by subclass to save settings to INI file
            databaseXmlDoc.Save(saveFileName);
        }

        public string[] GetTableNames()
        {
            HashSet<string> tableNames = new HashSet<string>();

            GetTables();
            foreach (TableSummary table in tableList)
            {
                tableNames.Add(table.TableName);
            }

            return tableNames.ToArray();
        }

        public TableSummary[] GetTables()
        {
            if (tableList == null || tableList.Count == 0)
            {
                // get a list of all the games
                XmlNodeList xmlGameList = databaseXmlDoc.GetElementsByTagName(TableSummary.xmlTable);
                tableList = new List<TableSummary>();
                for (int i = 0; i < xmlGameList.Count; i++)
                {
                    // populate the list of games with the game names
                    // the game name is an attribute of the xml node.
                    XmlNode gameNode = xmlGameList[i];
                    XmlAttributeCollection gameAttributes = gameNode.Attributes;
                    XmlNode gameNameNode = gameAttributes.GetNamedItem(TableSummary.xmlName);

                    TableSummary tableSummary = new TableSummary(gameNode);
                    tableList.Add(tableSummary);
                }
            }

            return tableList.ToArray();
        }

        public string[] GetManufacturerNames()
        {
            if (manufacturerList == null)
            {
                // get a list of all the game manufacturers to populate the combo boxe
                // using a hash set to avoid duplicates easily.
                XmlNodeList manufacturerNodeList = databaseXmlDoc.GetElementsByTagName(TableSummary.xmlManufacturer);
                HashSet<string> manufacturerSet = new HashSet<string>();
                for (int i = 0; i < manufacturerNodeList.Count; i++)
                {
                    if (manufacturerNodeList[i].InnerText != "")
                    {
                        manufacturerSet.Add(manufacturerNodeList[i].InnerText);
                    }
                }

                manufacturerList = manufacturerSet.ToList<string>();
            }

            return manufacturerList.ToArray();
        }

        public string[] GetTableTypes()
        {
            if (typeList == null)
            {

                // get a list of all the game types to populate the combo box
                // using a hash set to avoid duplicates easily.
                XmlNodeList typeNodeList = databaseXmlDoc.GetElementsByTagName(TableSummary.xmlType);
                HashSet<string> typeSet = new HashSet<string>();
                for (int i = 0; i < typeNodeList.Count; i++)
                {
                    if (typeNodeList[i].InnerText != "")
                    {
                        typeSet.Add(typeNodeList[i].InnerText);
                    }
                }

                typeList = typeSet.ToList<string>();
            }
            
            return typeList.ToArray();
        }

        public string[] GetTableYears()
        {
            if (yearList == null)
            {
                // get a list of all the game years to populate the combo box
                // using a hash set to avoid duplicates easily.
                XmlNodeList yearNodeList = databaseXmlDoc.GetElementsByTagName(TableSummary.xmlYear);
                HashSet<string> yearSet = new HashSet<string>();
                for (int i = 0; i < yearNodeList.Count; i++)
                {
                    if (yearNodeList[i].InnerText != "")
                    {
                        yearSet.Add(yearNodeList[i].InnerText);
                    }
                }
             
                yearList = yearSet.ToList<string>();
            }

            return yearList.ToArray();
        }

        public TableSummary GetTableSummary(string tableName)
        {
            if (databaseXmlDoc != null)
            {
                string xpathToTable = String.Format("descendant::game[@name={0}]", EncaseXpathString(tableName));
                XmlNode tableNode = databaseXmlDoc.SelectSingleNode(xpathToTable);
                if (tableNode != null)
                {
                    TableSummary tableSummary = new TableSummary(tableNode);
                    return tableSummary;
                }
            }

            return null;
        }

        protected override void RemoveItem(int index)
        {
            TableSummary tableSummary = this.Items[index];
            XmlNode tableNode = tableSummary.XmlNode;
            tableNode.ParentNode.RemoveChild(tableNode);

            base.RemoveItem(index);
        }

        public void Custom_AddingNew(object sender, AddingNewEventArgs e)
        {
            if (databaseXmlDoc != null)
            {
                XmlNode menuNode = databaseXmlDoc.SelectSingleNode("menu");
                if (menuNode != null)
                {
                    XmlAttribute nameAttribute = databaseXmlDoc.CreateAttribute(TableSummary.xmlName);
                    nameAttribute.Value = "Untitled Table 1";

                    XmlNode tableNode = databaseXmlDoc.CreateNode(XmlNodeType.Element, TableSummary.xmlTable, null);
                    tableNode.Attributes.Append(nameAttribute);

                    menuNode.AppendChild(tableNode);
                    e.NewObject = new TableSummary(tableNode);
                }
            }
        }


        public static string EncaseXpathString(string input)
        {
            // If we don't have any " then encase string in " 
            if (!input.Contains("\""))
                return String.Format("\"{0}\"", input);

            // If we have some " but no ' then encase in ' 
            if (!input.Contains("'"))
                return String.Format("'{0}'", input);

            // If we get here we have both " and ' in the string so must use Concat 
            StringBuilder sb = new StringBuilder("concat(");

            // Going to look for " as they are LESS likely than ' in our data so will minimise 
            // number of arguments to concat. 
            int lastPos = 0;
            int nextPos = input.IndexOf("\"");
            while (nextPos != -1)
            {
                // If this is not the first time through the loop then seperate arguments with , 
                if (lastPos != 0)
                    sb.Append(",");

                sb.AppendFormat("\"{0}\",'\"'", input.Substring(lastPos, nextPos - lastPos));
                lastPos = ++nextPos;

                // Find next occurance 
                nextPos = input.IndexOf("\"", lastPos);
            }

            sb.Append(")");
            return sb.ToString();
        } 

    }
}
